﻿using CluelessServer;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CluelessServer
{
    public partial class Form1 : Form
    {
        // DELEGATES
        public delegate void StringDelegate(String s);
        public delegate void UserEventDelegate(object s, UserChangeEventArgs e);
        public delegate void VoidDelegate();
        // END DELEGATES


        Network network;  // Handles all the network shit

        private Board board;  
        private CardDeck cardDeck;
        private Card solutionWeapon;
        private Card solutionCharacter;
        private Card solutionRoom;
        private CardDeck characterDeck;
        private PlayerList players;


        public Form1()
        {
            InitializeComponent();

            network = new Network();
            network.DebugMessage += NetworkDebugMessage;
            network.UserConnected += AddUser;
            network.UserDisconnected += DropUser;
            network.CharacterSelected += CharacterSelected;
            network.EndTurn += EndTurn;
            network.CharacterMoved += CharacterMoved;
            network.CharacterForcedMoved += CharacterForcedMoved;
            network.Suggestion += SuggestionMade;
            network.Disproved += Disprove;
            network.Accusation += Accusation;
            network.PlayerLost += PlayerLost;

            // Init the game
            cardDeck = new CardDeck(true);
            // Shuffle that shit!
            cardDeck.Shuffle();
            // Build the board
            board = new Board();

            // Gen a character list
            characterDeck = new CardDeck(false);
            characterDeck.GenerateCharacters();

            // Determine win state
            bool gotChar = false, gotRoom = false, gotWeap = false;
            foreach (Card c in cardDeck.Cards)
            {
                if (c.CardType == CardType.Character && !gotChar)
                {
                    solutionCharacter = c;
                    gotChar = true;
                }
                else if (c.CardType == CardType.Room && !gotRoom)
                {
                    solutionRoom = c;
                    gotRoom = true;
                }
                else if (c.CardType == CardType.Weapon && !gotWeap)
                {
                    solutionWeapon = c;
                    gotWeap = true;
                }

                if (gotChar && gotRoom && gotWeap)
                {
                    break;
                }
            }
            

            // Remove solution cards from the deck
            cardDeck.RemoveSolution(solutionCharacter, solutionRoom, solutionWeapon);

            // Init the player list
            players = new PlayerList();

            // ORDER OF OPERATIONS...
            // DealCards() 
            // BeginCharacterSelection()

        }

        /// <summary>
        /// Start the network comms for character selection
        /// </summary>
        private void BeginCharacterSelection()
        {
            network.SendMessage(players.Players.ElementAt(0).ClinetInformation, characterDeck.ToSelectionMessage(), null, MessageType.ChooseCharacter);
        }

        /// <summary>
        /// A character has been selected.  Record the data and request the next character
        /// </summary>
        private void CharacterSelected(object sender, CharacterSelectedEventArgs e)
        {
            players.GetCurrentPlayer().Character = characterDeck.GetCard(e.name);  // Assign the response character to the current player

            switch (players.GetCurrentPlayer().Character.Name)
            {
                case "Colonel Mustard":
                    players.GetCurrentPlayer().CurrentRoom = board.GetRoom("Lounge To Dining Room");
                    break;
                case "Professor Plum":
                    players.GetCurrentPlayer().CurrentRoom = board.GetRoom("Library To Study");
                    break;
                case "Mrs. Peacock":
                    players.GetCurrentPlayer().CurrentRoom = board.GetRoom("Conservatory To Library");
                    break;
                case "Miss Scarlet":
                    players.GetCurrentPlayer().CurrentRoom = board.GetRoom("Hall To Lounge");
                    break;
                case "Mr. Green":
                    players.GetCurrentPlayer().CurrentRoom = board.GetRoom("Ballroom To Conservatory");
                    break;
                case "Mrs. White":
                    players.GetCurrentPlayer().CurrentRoom = board.GetRoom("Kitchen To Ballroom");
                    break;
                default:
                    throw new Exception("Failed to detemine who the character was. " + players.GetCurrentPlayer().Character.Name);
                    break;
            }

            SyncCharacterData();

            characterDeck.Remove(players.GetCurrentPlayer().Character);  // Remove this option from the character deck

            Player currPlayer = players.GetNextPlayer();  // Get the next player
            if (currPlayer.Character != null)  // If the character has already been selected, we have gone through the entire list
            {
                // All characters have been selected
                Console.WriteLine("Character Selection Complete!!");
                WriteLog("Character Selection Complete ");

                StartTurn();
            }
            else
            {
                // Request the next character
                network.SendMessage(currPlayer.ClinetInformation, characterDeck.ToSelectionMessage(), null, MessageType.ChooseCharacter);
            }
            
        }

        public void StartTurn()
        {
            foreach (Player p in players.Players)
            {
                network.SendMessage(p.ClinetInformation, players.GetCurrentPlayer().Character.Name, null, MessageType.Turn);
            }

            WriteLog(players.GetCurrentPlayer().Character.Name + "\'s Turn!");
        }

        public void EndTurn(object sender, EndTurnEventArgs e)
        {
            WriteLog(players.GetCurrentPlayer().Character.Name + " " + e.message);
            players.GetNextPlayer();
            StartTurn();
        }

        /// <summary>
        /// Sends the list of play data to all active players
        /// </summary>
        private void SyncCharacterData()
        {
            String message = "";
            foreach (Player p in players.Players)
            {
                if (p != null)
                {
                    if (p.Character != null && p.CurrentRoom != null)
                    {
                        if (message == "")
                        {
                            message += p.Character.Name + ":" + p.CurrentRoom.Name;
                        }
                        else
                        {
                            message += ";" + p.Character.Name + ":" + p.CurrentRoom.Name;
                        }
                    }
                }
            }
            foreach (Player p in players.Players)
            {
                network.SendMessage(p.ClinetInformation, message, null, MessageType.SyncChar);
            }
           
        }

        /// <summary>
        /// Deals the cards to all players currently connected.  Will add the card locally and send a message to add it remotely to the clients..
        /// </summary>
        private void DealCards()
        {
            Player current = players.GetCurrentPlayer();

            foreach (Card c in cardDeck.Cards)
            {
                current.AddCard(c);
                network.SendMessage(current.ClinetInformation, c.Name, null, MessageType.AddCard);
                current = players.GetNextPlayer();

                // This runs FAR too fast for the network...  time to do something stupid!
                System.Threading.Thread.Sleep(200);
            }


            BeginCharacterSelection();
        }

        String suggestionCharacter = "", suggestionRoom = "", suggestionWeapon = "", suggestionSuggester = "";

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SuggestionMade(object sender, SuggestionEventArgs e)
        {
            Console.WriteLine("Suggestion Made");
            suggestionCharacter = e.message.Split(':')[0];
            suggestionRoom = e.message.Split(':')[1];
            suggestionWeapon = e.message.Split(':')[2];

            suggestionSuggester = e.suggester;

            network.SendMessage(players.GetNextPlayer().ClinetInformation, e.message, e.suggester, MessageType.Suggest);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Disprove(object sender, SuggestionEventArgs e)
        {
            if (e.message == "pass")
            {
                if (players.GetNextPlayer().Character.Name == suggestionSuggester)
                {
                    String disprover = players.GetCurrentPlayer().Character.Name;
                    while (players.GetNextPlayer().Character.Name != suggestionSuggester)
                    {
                    }
                    foreach (Player p in players.Players)
                    {
                        network.SendMessage(p.ClinetInformation, suggestionCharacter + ":" + suggestionRoom + ":" + suggestionWeapon + ":none:none", suggestionSuggester, MessageType.SuggestResponse);
                    }
                }
                else
                {
                    network.SendMessage(players.GetCurrentPlayer().ClinetInformation, suggestionCharacter + ":" + suggestionRoom + ":" + suggestionWeapon + ":none:none", suggestionSuggester, MessageType.Suggest);
                }
            }
            else
            {
                String disprover = players.GetCurrentPlayer().Character.Name;
                while (players.GetNextPlayer().Character.Name != suggestionSuggester)
                {
                }
               
                foreach (Player p in players.Players)
                {

                    network.SendMessage(p.ClinetInformation, suggestionCharacter + ":" + suggestionRoom + ":" + suggestionWeapon + ":" + e.message + ":" + disprover, suggestionSuggester, MessageType.SuggestResponse);
                    System.Threading.Thread.Sleep(200);
                }
                
            }
        }

        /// <summary>
        /// Debug bs
        /// </summary>
        /// <param name="s"></param>
        private void WriteLog(String s)
        {
            if (txtLog.InvokeRequired)
                txtLog.Invoke(new StringDelegate(this.WriteLog), s);
            else
                txtLog.Text += s;
        }

        void NetworkDebugMessage(object sender, DebugMessageEventArgs e)
        {
            WriteLog(e.message);
        }

        /// <summary>
        /// Sets the text box on the screen to the total number of palyers
        /// </summary>
        /// <param name="s"></param>
        void SetPlayerCount()
        {
            if (txtPlayerCount.InvokeRequired)
                txtPlayerCount.Invoke(new VoidDelegate(SetPlayerCount));
            else
            {
                txtPlayerCount.Text = players.NumberOfPlayers.ToString();

                btnStartGame.Enabled = (players.NumberOfPlayers >= 3);
            }
        }
        
        /// <summary>
        /// Adds the new user to the playerlist
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void AddUser(object sender, UserChangeEventArgs e)
        {
            if (lstUsers.InvokeRequired)
                lstUsers.Invoke(new UserEventDelegate(AddUser), sender, e);
            else
            {
                lstUsers.Items.Add(e.message);
                players.AddPlayer(new Player(e.clientInfo));
                SetPlayerCount();
            }
        }

        /// <summary>
        /// Removes the user from the playerlist
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void DropUser(object sender, UserChangeEventArgs e)
        {
            if (lstUsers.InvokeRequired)
                lstUsers.Invoke(new UserEventDelegate(DropUser), sender, e);
            else
            {
                lstUsers.Items.Remove(e.message);
                players.RemoverPlayer(players.GetPlayer(e.clientInfo.Name));
                SetPlayerCount();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStartGame_Click(object sender, EventArgs e)
        {
            DealCards();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CharacterMoved(object sender, CharacterMovedEventArgs e)
        {
            players.GetPlayer(e.character).CurrentRoom = board.GetRoom(e.location);

            SyncCharacterData();
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CharacterForcedMoved(object sender, CharacterMovedEventArgs e)
        {
            players.GetPlayer(e.character).CurrentRoom = board.GetRoom(e.location);

            SyncCharacterData();
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Accusation(object sender, SuggestionEventArgs e)
        {
            if (e.message.Split(':')[0] == solutionCharacter.Name && e.message.Split(':')[1] == solutionRoom.Name && e.message.Split(':')[2] == solutionWeapon.Name)
            {
                // WIN!

                foreach (Player p in players.Players)
                {
                    network.SendMessage(p.ClinetInformation, "win:" + solutionCharacter + ":" + solutionRoom + ":" + solutionWeapon, e.suggester, MessageType.AccuseResponse);
                    System.Threading.Thread.Sleep(200);
                }
            }
            else
            {
                // you lose, you piece of shit
                foreach (Player p in players.Players)
                {
                    network.SendMessage(p.ClinetInformation, "fail:" + e.message, e.suggester, MessageType.AccuseResponse);
                    System.Threading.Thread.Sleep(200);
                }

            }
        }

        private void PlayerLost(object sender, SuggestionEventArgs e)
        {
            foreach (Player p in players.Players)
            {
                network.SendMessage(p.ClinetInformation, "lost", e.message, MessageType.PlayerLost);
                System.Threading.Thread.Sleep(200);
            }
        }
    }
}
